package com.gframework.boot.mvc.controller.session.cas;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.AssertionHolder;
import org.jasig.cas.client.validation.Assertion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.filter.OncePerRequestFilter;

import com.gframework.annotation.EnableCasClientSecurity;
import com.gframework.boot.mvc.controller.session.ConversationHolder;
import com.gframework.boot.mvc.controller.session.ConversationIdentity;
import com.gframework.boot.mvc.controller.session.interceptor.ConversationCreater;
import com.gframework.boot.mvc.controller.session.interceptor.ConversationCreaterException;
import com.gframework.boot.mvc.controller.session.interceptor.ConversationInitInterceptor;

/**
 * 替换cas验证后session中的用户信息.
 * <p>想要扩展用户对象属性，可以新增 {@link ConversationInitInterceptor} 接口子类并被spring管理。
 * <p>通过{@link EnableCasClientSecurity}注解启动
 * <p>cas官方可使用{@link AssertionHolder}类来获取当前登录用户
 * 
 * @since 1.0.0
 * @author Ghwolf
 * 
 * @see AbstractCasFilter#CONST_CAS_ASSERTION
 * @see Assertion
 * @see ConversationIdentity
 * @see ConversationCreater
 * @see ConversationInitInterceptor
 * @see EnableCasClientSecurity
 * @see ConversationHolder
 */
@ConditionalOnClass({Assertion.class,AbstractCasFilter.class,AssertionHolder.class})
public class CasSessionUserCreateFilter extends OncePerRequestFilter{
	private static final Logger slf4j = LoggerFactory.getLogger(CasSessionUserCreateFilter.class);
	
	/**
	 * 用户信息初始化操作类
	 */
	private ConversationInitInterceptor[] casSessionUserInit ;
	/**
	 * 用户信息创建类
	 */
	@Autowired
	private ConversationCreater conversationCreater ;

	public CasSessionUserCreateFilter(ConversationInitInterceptor[] casSessionUserInit) {
		if (casSessionUserInit == null) {
			casSessionUserInit = new ConversationInitInterceptor[0];
		} else {
			this.casSessionUserInit = casSessionUserInit;
		}
	}
	
	/**
	 * 默认的用户会话信息创建拦截器
	 */
	@Bean
	@ConditionalOnMissingBean(value=ConversationCreater.class)
	public ConversationCreater defaultConversationCreater() {
		return new DefaultConversationCreater();
	}
	
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		final HttpSession session = request.getSession(false);
        Assertion assertion = session != null ? (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : null;
        if (assertion != null && !(assertion instanceof ConversationIdentity)) {
        	assertion = this.doReplace(assertion,request);
        	if (slf4j.isDebugEnabled()) {
        		slf4j.debug("CAS 的 session用户信息 assertion 替换成功：\n{}",assertion);
        	}
        	session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION,assertion);
        }
        try {
        	// 注册到holder中
        	ConversationHolder.setConversation((ConversationIdentity)assertion);
        	filterChain.doFilter(request, response);
        } finally {
        	ConversationHolder.clear();
        }
	}
	
	/**
	 * 进行替换操作
	 */
	private Assertion doReplace(Assertion assertion,HttpServletRequest request) {
		ConversationIdentity user = conversationCreater.createConversation(assertion,request);
		if (user == null) {
			throw new ConversationCreaterException(conversationCreater.getClass().getName() + "创建的用户对象为null，请检查代码逻辑是否合理，ConversationCreater接口返回值不能为null。");
		}
		for (ConversationInitInterceptor handler : this.casSessionUserInit) {
			if (handler.canInterceptor(user.getClass())) {
				user = handler.init(user, request);
			}
		}
		if (user instanceof Assertion) {
			return (Assertion) user ;
		} else {
			return new AssertionWrapper(user, assertion);
		}
	}

}
